package com.hitqz.robot.netty.service.impl;


import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.hitqz.robot.api.business.model.vo.form.FormConfigModalVo;
import com.hitqz.robot.api.business.model.vo.form.FormFieldConfigModalVo;
import com.hitqz.robot.api.common.entity.*;
import com.hitqz.robot.api.dispatch.model.MessageDto;
import com.hitqz.robot.api.dispatch.protocol.ProtocolEnum;
import com.hitqz.robot.api.dispatch.protocol.dto.*;
import com.hitqz.robot.common.core.util.R;
import com.hitqz.robot.netty.ChannelCache;
import com.hitqz.robot.netty.service.Send2ServerService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author xupkun
 * @date 2024/6/13
 */
@Service
@Slf4j
@AllArgsConstructor
public class Send2ServerServiceImpl implements Send2ServerService {

    private static final GeometryFactory geometryFactory = new GeometryFactory();


    @Override
    public void send2ServerData(Integer cmd, String data) {
        MessageDto messageDto = new MessageDto();
        messageDto.setCmd(cmd.shortValue());
        messageDto.setData(data.getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
    }

    @Override
    public void commonReply(Integer sn, R result) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(sn);
        messageDto.setCmd(ProtocolEnum.RESULT.getCmd());
        String data = JSONObject.toJSONString(result);
        messageDto.setData(data.getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
    }

    @Override
    public void syncRobotInfo(Integer sn, RobotDto robotDto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(sn);
        messageDto.setCmd(ProtocolEnum.ROBOT_INFO.getCmd());
        messageDto.setData(JSONObject.toJSONString(robotDto).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
    }

    @Override
    public void syncRobotMapInfo(Integer sn, RobotMapDto robotMapDto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(sn);
        messageDto.setCmd(ProtocolEnum.ROBOT_MAP_INFO.getCmd());
        messageDto.setData(JSONObject.toJSONString(robotMapDto).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
    }

    @Override
    public void syncRobotMapPosInfo(Integer sn, RobotMapPosDto robotMapPosDto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(sn);
        messageDto.setCmd(ProtocolEnum.ROBOT_MAP_POS_INFO.getCmd());
        messageDto.setData(JSONObject.toJSONString(robotMapPosDto).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
    }

    @Override
    public void syncRobotMapAreaInfo(Integer sn, RobotMapAreaDto robotMapAreaDto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(sn);
        messageDto.setCmd(ProtocolEnum.ROBOT_AREA_INFO.getCmd());
        messageDto.setData(JSONObject.toJSONString(robotMapAreaDto).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
    }

    @Override
    public List<RobotMapPosDto> applyPath(Robot robot, LinkedList<RobotMapPos> pathPositions) {
        // 构建MessageDto对象
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setCmd(ProtocolEnum.ROBOT_APPLY_TARGET_POS.getCmd());

        // 构建RobotApplyPathDto对象
        RobotApplyPathDto robotApplyPathDto = new RobotApplyPathDto();
        robotApplyPathDto.setClientId(robot.getRobotSn());
        if (!pathPositions.isEmpty()) {
            robotApplyPathDto.setCode(pathPositions.getLast().getCode());
        }

        // 合并路径编码列表的生成与MessageDto数据设置
        try {
            robotApplyPathDto.setPathList(pathPositions.stream()
                    .map(t-> {
                        RobotMapPosDto robotMapPosDto = new RobotMapPosDto();
                        BeanUtil.copyProperties(t, robotMapPosDto, false);
                        return robotMapPosDto;
                    })
                    .collect(Collectors.toCollection(LinkedList::new)));

            // 优化距离计算
            if (!pathPositions.isEmpty()) {
                LinkedList<Double> distances = calculateDistances(pathPositions, geometryFactory);
                robotApplyPathDto.setCodeX(pathPositions.getLast().getPosX());
                robotApplyPathDto.setCodeY(pathPositions.getLast().getPosY());
                robotApplyPathDto.setPathDistances(distances);
            }
            byte[] dataBytes = JSONObject.toJSONString(robotApplyPathDto).getBytes(StandardCharsets.UTF_8);
            messageDto.setData(dataBytes);

            // 发送消息
            ChannelCache.send2All(messageDto);

            // 返回路径编码列表
            return robotApplyPathDto.getPathList();
        } catch (Exception e) {
            // 更加详细的异常处理
            throw new RuntimeException("Failed to process data.", e);
        }
    }

    private LinkedList<Double> calculateDistances(LinkedList<RobotMapPos> pathPositions, GeometryFactory geometryFactory) {
        LinkedList<Double> distances = new LinkedList<>();
        if (pathPositions.size() > 1) {
            Point lastPoint = null;
            for (RobotMapPos pos : pathPositions) {
                Point currentPoint = geometryFactory.createPoint(new Coordinate(pos.getPosX(), pos.getPosY(), pos.getYaw()));
                if (lastPoint != null) {
                    distances.add(lastPoint.distance(currentPoint));
                }
                lastPoint = currentPoint;
            }
        }
        return distances;
    }





    @Override
    public boolean syncTickets(Robot robot, Tickets tickets, List<TicketsTask> tasks) {
        RobotTicketsDto robotTicketsDto = new RobotTicketsDto();
        BeanUtil.copyProperties(tickets, robotTicketsDto, false);
        Optional.ofNullable(tickets.getId()).ifPresent(id -> robotTicketsDto.setClientTicketsId(id.intValue()));
        List<RobotTicketsTaskDto> taskDtos = new ArrayList<>();
        for (TicketsTask ticketsTask : tasks) {
            RobotTicketsTaskDto taskDto = new RobotTicketsTaskDto();
            BeanUtil.copyProperties(ticketsTask, taskDto);
            Optional.ofNullable(ticketsTask.getId()).ifPresent(id -> taskDto.setClientTaskId(id.intValue()));
            Optional.ofNullable(ticketsTask.getServerTaskId()).ifPresent(serverId -> taskDto.setServerId(serverId.intValue()));
            taskDtos.add(taskDto);
        }
        robotTicketsDto.setTickets(taskDtos);
        log.info("[tickets]upload tickets id in server:{}",tickets.getServerId());
        robotTicketsDto.setServerId(tickets.getServerId());
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setCmd(ProtocolEnum.ROBOT_TICKETS.getCmd());
        try {
            String jsonString = JSONObject.toJSONString(robotTicketsDto);
            log.info("[tickets]upload tickets content:\n{}",jsonString);
            messageDto.setData(jsonString.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("Error serializing JSON object:{}",e.getMessage());
            return false;
        }
        ChannelCache.send2All(messageDto);
        return true;
    }


    @Override
    public boolean syncSysWarning(Robot robot, RobotWarningDto sysWarning) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(ProtocolEnum.ROBOT_WARNING.getCmd());
        messageDto.setCmd(ProtocolEnum.ROBOT_WARNING.getCmd());
        messageDto.setData(JSONObject.toJSONString(sysWarning).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
        return true;
    }

    @Override
    public boolean syncTasks(Robot robot, FormConfigModalVo vo) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setCmd(ProtocolEnum.TASK_CONTENT.getCmd());
        List<FormFieldConfigModalDto> modelDtos = Lists.newArrayList();
        FormConfigModalDto dto = new FormConfigModalDto();
        for (FormFieldConfigModalVo field : vo.getFields()) {
            FormFieldConfigModalDto modalDto = new FormFieldConfigModalDto();
            BeanUtil.copyProperties(field, modalDto);
            modelDtos.add(modalDto);
        }
        dto.setFormTitle(vo.getFormTitle());
        dto.setFormName(vo.getFormName());
        dto.setRobotSn(robot.getRobotSn());
        dto.setFields(modelDtos);
        messageDto.setData(JSONObject.toJSONString(dto).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
        return false;
    }

    @Override
    public boolean sendLog(String clientId, String randomId, String message) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setCmd(ProtocolEnum.REPORT_LOG_MESSAGE.getCmd());
        ReportLogMessageDto reportLogMessageDto = new ReportLogMessageDto();
        reportLogMessageDto.setClientId(clientId);
        reportLogMessageDto.setMessage(message);
        reportLogMessageDto.setRandomId(randomId);
        messageDto.setData(JSONObject.toJSONString(reportLogMessageDto).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
        return true;
    }

    @Override
    public boolean uploadLogFile(String clientId, String fileName) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setCmd(ProtocolEnum.UPLOAD_LOG_FILE.getCmd());
        UploaderLogFileDto uploaderLogFileDto = new UploaderLogFileDto();
        uploaderLogFileDto.setClientId(clientId);
        uploaderLogFileDto.setLogFile(fileName);
        messageDto.setData(JSONObject.toJSONString(uploaderLogFileDto).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
        return true;
    }

    @Override
    public boolean uploadBatteryLevel(String clientId, BatteryDataInfo dataInfo) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setCmd(ProtocolEnum.REPORT_BATTERY.getCmd());
        ReportBatteryLevelDto batteryLevelDto = new ReportBatteryLevelDto();
        batteryLevelDto.setClientId(clientId);
        batteryLevelDto.setBattery(dataInfo.getBattery());
        batteryLevelDto.setCurrent(dataInfo.getCurrent());
        batteryLevelDto.setStatisticalTime(dataInfo.getStatisticalTime());
        batteryLevelDto.setVoltage(dataInfo.getVoltage());
        messageDto.setData(JSONObject.toJSONString(batteryLevelDto).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
        return true;
    }


}
